package com.guanmi.ethereum.controller;

import com.guanmi.ethereum.constants.GlobalConstants;
import com.guanmi.ethereum.domain.WalletResponseEntity;
import com.guanmi.ethereum.service.BloggerCoin;
import com.guanmi.ethereum.service.EthereumService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.web3j.abi.FunctionEncoder;
import org.web3j.abi.TypeReference;
import org.web3j.abi.datatypes.Address;
import org.web3j.abi.datatypes.Function;
import org.web3j.abi.datatypes.Type;
import org.web3j.abi.datatypes.generated.Uint256;
import org.web3j.crypto.Credentials;
import org.web3j.crypto.RawTransaction;
import org.web3j.crypto.TransactionEncoder;
import org.web3j.protocol.Web3j;
import org.web3j.protocol.core.DefaultBlockParameterName;
import org.web3j.protocol.core.RemoteCall;
import org.web3j.protocol.core.methods.response.EthGasPrice;
import org.web3j.protocol.core.methods.response.EthGetTransactionCount;
import org.web3j.protocol.core.methods.response.EthSendTransaction;
import org.web3j.protocol.core.methods.response.EthTransaction;
import org.web3j.protocol.http.HttpService;
import org.web3j.tx.Contract;
import org.web3j.utils.Numeric;

import java.io.IOException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ExecutionException;

@RestController
@RequestMapping(value = "/eth")
public class EthereumController {
    private static final Logger L = LoggerFactory.getLogger(EthereumController.class);
    @Autowired
    private EthereumService ethereumService;

    @Autowired
    private BloggerCoin bloggerCoin;

    @ResponseBody
    @RequestMapping(value = "balanceOfContract", method = RequestMethod.POST)
    public BigDecimal getContractBalance(String address) throws Exception {
        String erc20Balance = this.getERC20Balance(bloggerCoin, address);
        return new BigDecimal(erc20Balance);
    }

    /**
     * 查询erc 20 代币价格
     * @throws Exception
     */
    public String getERC20Balance(Contract contract, String userAddress) throws Exception {
        RemoteCall<BigInteger> balanceOf = ((BloggerCoin) contract).balanceOf(userAddress);
        BigInteger send2 = balanceOf.send();
        return toDecimal(4, send2);
    }

    /**
     * 转换成符合 decimal 的数值
     * @param decimal
     * @param
     * @return
     */
    public String toDecimal(int decimal,BigInteger integer){
        StringBuffer sbf = new StringBuffer("1");
        for (int i = 0; i < decimal; i++) {
            sbf.append("0");
        }
        String balance = new BigDecimal(integer).divide(new BigDecimal(sbf.toString()), 4, BigDecimal.ROUND_DOWN).toPlainString();
        return balance;
    }

    @ResponseBody
    @RequestMapping(value = "transferContract", method = RequestMethod.POST)
    public void transferContract(String from, String to, String privateKey, String contractAddress, String value) throws Exception {
        String hash = this.transferErc20Token(from, to, new BigInteger(value), privateKey, contractAddress);
        L.info("hash: {}", hash);
    }

    /**
     * erc20代币转账
     *
     * @param from            转账地址
     * @param to              收款地址
     * @param value           转账金额
     * @param privateKey      转账这私钥
     * @param contractAddress 代币合约地址
     * @return 交易哈希
     */
    public String transferErc20Token(String from, String to, BigInteger value, String privateKey, String contractAddress) throws ExecutionException, InterruptedException, IOException {
        Web3j web3j = Web3j.build(new HttpService("http://localhost:7545"));
		//加载转账所需的凭证，用私钥
		Credentials credentials = Credentials.create(privateKey);
		//获取nonce，交易笔数
		BigInteger nonce;
		EthGetTransactionCount ethGetTransactionCount = web3j.ethGetTransactionCount(from, DefaultBlockParameterName.PENDING).send();
		if (ethGetTransactionCount == null) {
			return null;
		}
		nonce = ethGetTransactionCount.getTransactionCount();
		//gasPrice和gasLimit 都可以手动设置
		BigInteger gasPrice;
		EthGasPrice ethGasPrice = web3j.ethGasPrice().sendAsync().get();
		if (ethGasPrice == null) {
			return null;
		}
		gasPrice = ethGasPrice.getGasPrice();
		//BigInteger.valueOf(4300000L) 如果交易失败 很可能是手续费的设置问题
		BigInteger gasLimit = BigInteger.valueOf(60000L);
		//ERC20代币合约方法
		value = value.multiply(new BigInteger("10000"));
		Function function = new Function(
				"transfer",
				Arrays.asList(new Address(to), new Uint256(value)),
				Collections.singletonList(new TypeReference<Type>() {
				}));
		//创建RawTransaction交易对象
		String encodedFunction = FunctionEncoder.encode(function);
		RawTransaction rawTransaction = RawTransaction.createTransaction(nonce, gasPrice, gasLimit,
				contractAddress, encodedFunction);

		//签名Transaction
		byte[] signMessage = TransactionEncoder.signMessage(rawTransaction, credentials);
		String hexValue = Numeric.toHexString(signMessage);
		//发送交易
		EthSendTransaction ethSendTransaction = web3j.ethSendRawTransaction(hexValue).sendAsync().get();
		String hash = ethSendTransaction.getTransactionHash();
		return hash;
	}

    @ResponseBody
    @RequestMapping(value = "getAccountList", method = RequestMethod.POST)
    public WalletResponseEntity<List<String>> getAccountlist() {
        WalletResponseEntity<List<String>> result = new WalletResponseEntity<List<String>>();
        try {
            return ethereumService.getAccountlist();
        } catch (Exception e) {
            L.error("获取账户列表", e.getMessage());
            result.setMsg("系统异常");
            result.setStatus(GlobalConstants.SYSTEM_ERROR);
            return result;
        }
    }

    /**
     * 创建账号
     * @param password:密码
     * @return String
     * @Title: generateNewWalletFile
     * @Description: 创建账户
     */
    @ResponseBody
    @RequestMapping(value = "generateNewWalletFile/{password}", method = RequestMethod.POST)
    public WalletResponseEntity<Credentials> generateNewWalletFile(@PathVariable("password") String password) {
        WalletResponseEntity<Credentials> result = new WalletResponseEntity<Credentials>();
        try {
            return ethereumService.generateNewWalletFile(password);
        } catch (Exception e) {
            L.error("创建账户", e.getMessage());
            result.setMsg("系统异常");
            result.setStatus(GlobalConstants.SYSTEM_ERROR);
            return result;
        }
    }

    /**
     * 获取用户eth余额
     * @param address:账户
     * @return BigInteger
     */
    @ResponseBody
    @RequestMapping(value = "getBalance/{address}", method = RequestMethod.POST)
    public WalletResponseEntity<BigInteger> getBalance(@PathVariable("address") String address) {
        WalletResponseEntity<BigInteger> result = new WalletResponseEntity<BigInteger>();
        try {
            return ethereumService.getBalance(address);
        } catch (Exception e) {
            L.error("获取用户余额", e.getMessage());
            result.setMsg("系统异常");
            result.setStatus(GlobalConstants.SYSTEM_ERROR);
            return result;
        }
    }

    /**
     * @param address:账户
     * @return BigInteger
     * @Title: getTransactionCount
     * @Description: 获取用户交易记录数
     */
    @ResponseBody
    @RequestMapping(value = "getTransactionCount/{address}", method = RequestMethod.POST)
    public WalletResponseEntity<BigInteger> getTransactionCount(@PathVariable("address") String address) {
        WalletResponseEntity<BigInteger> result = new WalletResponseEntity<BigInteger>();
        try {
            return ethereumService.getTransactionCount(address);
        } catch (Exception e) {
            L.error("获取用户交易记录数", e.getMessage());
            result.setMsg("系统异常");
            result.setStatus(GlobalConstants.SYSTEM_ERROR);
            return result;
        }
    }

    /**
     * @param transactionHash:交易的hash值
     * @return EthTransaction
     * @Title: getTransaction
     * @Description: 获取交易信息
     */
    @ResponseBody
    @RequestMapping(value = "getTransaction/{transactionHash}", method = RequestMethod.POST)
    public WalletResponseEntity<EthTransaction> getTransaction(@PathVariable("transactionHash") String transactionHash) {
        WalletResponseEntity<EthTransaction> result = new WalletResponseEntity<EthTransaction>();
        try {
            return ethereumService.getTransaction(transactionHash);
        } catch (Exception e) {
            L.error("获取交易信息", e.getMessage());
            result.setMsg("系统异常");
            result.setStatus(GlobalConstants.SYSTEM_ERROR);
            return result;
        }
    }

    /**
     * @param password：密码
     * @return String
     * @throws
     * @Description: 获取用户的账户
     */
    @ResponseBody
    @RequestMapping(value = "getAddress", method = RequestMethod.POST)
    public WalletResponseEntity<String> getAddress(String password) {
        WalletResponseEntity<String> result = new WalletResponseEntity<String>();
        try {
            //密钥文件地址
            String walletfile = "D://eth//keystore//UTC--2021-06-17T01-45-43.750000000Z--60351e2ec1208b1221229fed181acfdf893ab387.json";
            return ethereumService.getAddress(walletfile, password);
        } catch (Exception e) {
            L.error("获取用户的账户", e.getMessage());
            result.setMsg("系统异常");
            result.setStatus(GlobalConstants.SYSTEM_ERROR);
            return result;
        }
    }


}
